﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;

using BizElements.Core.ProviderComponents;

namespace BizElements.Core
{
    /// <summary>Fills a collection of built-in .NET primitive types such as numeric, date and textual types.</summary>
    public sealed class SingleFieldReader<T> : IObjectReader
        where T : IConvertible
    {
        /// <summary>Initializes a new instance of <b>SingleFieldReader</b> class.</summary>
        public SingleFieldReader()
        {
        }

        /// <summary>Initializes a new instance of <b>SingleFieldReader</b> class.</summary>
        /// <param name="ignoreNulls">Specifies whether the <see cref="FillCollection(IDataReader, IList)"/> method should ignore and skip null values.</param>
        public SingleFieldReader(bool ignoreNulls)
        {
            this.IgnoreNulls = ignoreNulls;
        }

        /// <summary>Gets or sets the values which specifies whether the <see cref="FillCollection(IDataReader, IList)"/> method should ignore and skip null values.</summary>
        public bool IgnoreNulls { get; set; }

        /// <summary>Converts retrieved data to target type and fills colledction.</summary>        
        /// <param name="openedDataReader">Opened data reader that contains all fields required to build objects.</param>
        /// <param name="collection">A collection to fill with objects built from data.</param>
        public void FillCollection(IDataReader openedDataReader, IList collection)
        {
            ICollection<T> genericCollection = collection as ICollection<T>;
            if (genericCollection != null)
            {
                if (this.IgnoreNulls)
                    FillGenericCollectionIgnoringNulls(openedDataReader, genericCollection);
                else
                    FillGenericCollection(openedDataReader, genericCollection);
            }
            else
            {
                if (this.IgnoreNulls)
                    FillNonGenericListIgnoringNulls(openedDataReader, collection);
                else
                    FillNonGenericList(openedDataReader, collection);
            }
        }

        private static void FillGenericCollection(IDataReader openedDataReader, ICollection<T> collection)
        {
            while (openedDataReader.Read())
            {
                T value = (T)Convert.ChangeType(openedDataReader.GetValue(0), typeof(T));
                collection.Add(value);
            }
        }

        private static void FillNonGenericList(IDataReader openedDataReader, IList collection)
        {
            while (openedDataReader.Read())
            {
                object value = openedDataReader.GetValue(0);
                collection.Add(value);
            }
        }

        private static void FillGenericCollectionIgnoringNulls(IDataReader openedDataReader, ICollection<T> collection)
        {
            object currVal;
            while (openedDataReader.Read())
            {
                currVal = openedDataReader.GetValue(0);
                if (!Convert.IsDBNull(currVal))
                {
                    T value = (T)Convert.ChangeType(currVal, typeof(T));
                    collection.Add(value);
                }
            }
        }

        private static void FillNonGenericListIgnoringNulls(IDataReader openedDataReader, IList collection)
        {
            object currVal;
            while (openedDataReader.Read())
            {
                currVal = openedDataReader.GetValue(0);
                if (!Convert.IsDBNull(currVal))
                    collection.Add(currVal);
            }
        }
    }
}